#include "minet_socket.h"
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;

#define BUFFERSIZE 1024
#define FILENAMESIZE 100

int handle_connection(int sock);
inline void checkRV(int rt, const char * err, int sock = 0);
inline void sendMsg(int sock, char *buf, long n);

int main(int argc, char * argv[]) {
  int server_port = -1;
  int rc          =  0;
  int sock        = -1;

  struct sockaddr_in addr;
  const static int BACKLOG = 5;//number of connection can be waiting

  /* parse command line args */
  if (argc != 3) {
    fprintf(stderr, "usage: http_server1 k|u port\n");
    exit(-1);
  }

  server_port = atoi(argv[2]);

  if (server_port < 1500) {
    fprintf(stderr, "INVALID PORT NUMBER: %d; can't be < 1500\n", server_port);
    exit(-1);
  }
    
  /*init*/
  if(toupper(argv[1][0]) == 'K') {
    minet_init(MINET_KERNEL);
  }
  else if(toupper(argv[1][0]) == 'U') {
    minet_init(MINET_USER);
  }
  else {
    fprintf(stderr, "usage: http_server1 k|u port\n");
    exit(-1);
  }

  /* initialize and make socket */
  checkRV(sock = minet_socket(SOCK_STREAM), "Unable to create socket\n");

  /* set server address*/
  bzero(&addr, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(server_port);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
  /* bind listening socket */
  checkRV(minet_bind(sock, &addr), "Unable to bind socket\n",sock);

  /* start listening */
  checkRV(minet_listen(sock, BACKLOG), "Unable to listen\n",sock);
  /* connection handling loop: wait to accept connection */
  while (1) {
    int new_sock = minet_accept(sock,&addr);
    //cout<<"-------New Connection Accepted-----------"<<endl;
    if(new_sock <= 0) {
      fprintf(stderr, "Error on accept");
      continue;
    }
    /* handle connections */
    rc = handle_connection(new_sock);
  }
  
  minet_deinit();
}

int handle_connection(int sock) {
  bool ok = false;
  string strReq;
  string strFilename;
  char buffer[BUFFERSIZE];//create buffer

  static char * ok_response_f = "HTTP/1.0 200 OK\r\n"	\
    "Content-type: text/plain\r\n"			\
    "Content-length: %d \r\n\r\n";
  static const int ok_response_len = strlen(ok_response_f);
  static char * notok_response = "HTTP/1.0 404 FILE NOT FOUND\r\n"	\
    "Content-type: text/html\r\n\r\n"			\
    "<html><body bgColor=black text=white>\n"		\
    "<h2>404 FILE NOT FOUND</h2>\n"
    "</body></html>\n";
  static const int notok_response_len = strlen(notok_response);
    
  /* first read loop -- get request and headers*/
  int size = 0;
  do {    
    buffer[size] = '\0';
    strReq += buffer;
    if(strReq.size()>=4 && 0 == strReq.compare(strReq.size()-4,4,"\r\n\r\n")) {
      break;
    }
    size = minet_read(sock, buffer,BUFFERSIZE);
    if(size == 0) {
      fprintf(stderr, "Connection Closed\n");
      return -1;
    }
    else if(size == -1) {//assuming this will never happen
      fprintf(stderr, "Blocked!\n");
      return -1;
    }
  }while(size > 0);
    
  /* parse request to get file name */
  /* Assumption: this is a GET request and filename contains no spaces*/
  //cout<<"Msg Received: "<<strReq<<endl;
  strFilename = strReq.substr(4,strReq.find(" ", 4)-4);
  if(strFilename[0] == '\\' || strFilename[0] == '/') 
    strFilename = strFilename.substr(1); //get rid of leading / or \
  cout<<strFilename<<endl;
  
  /* try opening the file */
  ifstream fin;// input file stream
  fin.open(strFilename,ios::in);//open file
  strReq.clear();
  if(fin.is_open()) {
    string tmp;
    while(fin.good()) {
      getline(fin, tmp);
      strReq += tmp;
      strReq += "\n";
    }
    ok = true;
  }else {//fail to open file
    ok =false;
  }
  fin.close();
  //cout<<"file content:\n"<<strReq<<endl;

  /* send response */
  if (ok) {
    /* send headers */
    char header[ok_response_len + 20];
    sprintf(header, ok_response_f, strReq.size());
    //cout<<strlen(header)<<endl<<header<<endl;
    sendMsg(sock, header, strlen(header));
    /* send file */
    char *contentTmp = new char[strReq.size()+1];//create a tmp buffer
    strcpy(contentTmp, strReq.data());
    contentTmp[strReq.size()]= '\0';
    sendMsg(sock, contentTmp,strReq.size());
    delete contentTmp;
    
  } else {
    sendMsg(sock, notok_response, notok_response_len);
  }
    
  /* close socket and free space */
  minet_close(sock);
  minet_deinit();  
  if(ok) {
    return 0;
  } else {
    return -1;
  }
}

/*check return value*/
inline void checkRV(int rt, const char * err, int sock) {
  if(rt < 0) {
    fprintf(stderr, err);
    minet_perror("Reason: ");
    minet_deinit();
    if(sock > 0) {
      minet_close(sock);
    }
    exit(-1);
  }
}

/*inline function for sending msg over socket*/
inline void sendMsg(int sock, char *buf, long n) {
  long totalByteSend = 0;
  while(totalByteSend < n) {
    int size = minet_write(sock, buf+totalByteSend, n-totalByteSend);
    if(size > 0) {
      totalByteSend += size;
    }
  }
}
